#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */ 
#endregion

using System;
using System.Diagnostics;
using System.Collections.Generic;
using Adoor.Expression;
using Adoor.Expression.AST;
using Adoor.Object.Domain;
using Adoor.Relational;
using Adoor.Object;
using Adoor.Object.Query;
using Adoor.Object.Entity;
using System.Data;

namespace Adoor.Object.Relational
{
	public class RelationalQueryGenerator: IObjectPathEvaluator
	{
		public static RelationalQuery GenerateQuery(MappingStrategyEnum mappingKind, ObjectQuery query)
		{
			ObjectPath op = query.Path;

			switch(query.QueryType)
			{
				case QueryType.Select:
					return GenerateQuerySelect(mappingKind, query);					
				case QueryType.Update:
					return GenerateQueryUpdate(mappingKind, query);
				case QueryType.Insert:
					return GenerateQueryInsert(mappingKind, query);
				case QueryType.Delete:
					return GenerateQueryDelete(mappingKind, query);
				default:
					throw new NotImplementedException();
			}
		}

		#region update
		public static RelationalQuery GenerateQueryUpdate(MappingStrategyEnum mappingKind, ObjectQuery query)
		{
			ScanPath modPath = (ScanPath) query.UpdatePath;
			IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)modPath.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
			
            IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
			ICollection<EntityData> entityDatas = mappingStrategy.AllChildsArray;

            List<RelationalQuery> alResult = new List<RelationalQuery>();
			bool first = true;
			foreach(EntityData ed in entityDatas)
			{
				alResult.AddRange(generateQueryUpdate(mappingKind, query, ed, first));
				first = false;
			}
			return new UpdateQuery(alResult);
		}

		private static ICollection<RelationalQuery> generateQueryUpdate(MappingStrategyEnum mappingKind, ObjectQuery query, EntityData ed, bool first)
		{
			UpdateQuery uqDirectAcces = null;
            Dictionary<EntityData, UpdateQuery> htInverseAcces = new Dictionary<EntityData, UpdateQuery>();
            List<UpdateQuery> alIntTable = new List<UpdateQuery>();
			RelationalOperator stdop = RelationalQueryGenerator.GenerateOperator(mappingKind, query.Path);
			
			SetExpression seOid = null;

			IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider) ed.GetService(typeof(IEntityMappingStrategyProvider));
			IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);						
						
			ExpressionName enOid = mappingStrategy.KeyMappingStrategy.GetOidExpression(query.ModificationEntity);
			seOid = new SetExpression(enOid, null, SetExpressionModes.Assign);
			
			foreach(SetExpression se in query.SetExpressions)
			{
				IList<SelectExpression> newCols;
				ExpressionName en = se.SetExpressionName;
				ExpressionNode expr2 = (ExpressionName) ExpressionMapper.Evaluate(se.SetExpressionName, query.Path);
				ExpressionName en2 = (ExpressionName) ExpressionExpander.Evaluate(expr2, query.Path, mappingKind, out newCols);
				
				ExpressionNode exprVal = ExpressionMapper.Evaluate(se.ValueExpression, query.Path);
				ExpressionNode enVal = ExpressionExpander.Evaluate(exprVal, query.Path, mappingKind, out newCols);
				
				//se.SetExpressionName = en2;
				SetExpression newSe = new SetExpression(en2, enVal, se.SetExpressionMode);
				
				if(ed.PropertyInfos.ContainsKey(en.Value))
				{
					if(uqDirectAcces == null)
					{
						provider = (IEntityMappingStrategyProvider)ed.GetService(typeof(IEntityMappingStrategyProvider));
						mappingStrategy = provider.GetMappingStrategy(mappingKind);
						ScanQuery sq = (ScanQuery) mappingStrategy.ProperInstancesQuery;

						uqDirectAcces = new UpdateQuery(sq.SourceName
							, new SetExpression[] { seOid, newSe }
							,UpdateQuery.UpdateQueryTypes.Update, stdop);
					}
					else
						uqDirectAcces.SetExpressions.Add(newSe);							
				}
				else if(ed.ReferenceInfos.ContainsKey(en.Value))
				{
					ReferenceData rd = ed.ReferenceInfos[en2.Value];
					if(rd.IsSingleReference)
					{
						if(rd.SourceData == ed)
						{
							if(uqDirectAcces == null)
							{
								provider = (IEntityMappingStrategyProvider)ed.GetService(typeof(IEntityMappingStrategyProvider));
								mappingStrategy = provider.GetMappingStrategy(mappingKind);
								ScanQuery sq = (ScanQuery) mappingStrategy.ProperInstancesQuery;

								uqDirectAcces = new UpdateQuery(sq.SourceName
									, new SetExpression[] {seOid, newSe }
									,UpdateQuery.UpdateQueryTypes.Update, stdop);
							}
							else
								uqDirectAcces.SetExpressions.Add(newSe);
						}
						else
						{
							UpdateQuery uq = (UpdateQuery) htInverseAcces[rd.TargetData];
							if(uq == null)
							{
								provider = (IEntityMappingStrategyProvider)rd.TargetData.GetService(typeof(IEntityMappingStrategyProvider));
								mappingStrategy = provider.GetMappingStrategy(mappingKind);
								ScanQuery sq = (ScanQuery) mappingStrategy.ProperInstancesQuery;

								uq = new UpdateQuery(sq.SourceName
									, new SetExpression[] {seOid, newSe }
									,UpdateQuery.UpdateQueryTypes.Update, stdop);

								htInverseAcces[rd.TargetData] = uq;
							}
							else
								uq.SetExpressions.Add(newSe);
						}
					}
					else
					{
						if(first)
						{
							if(newSe.SetExpressionMode != SetExpressionModes.Add 
								&& newSe.SetExpressionMode != SetExpressionModes.Remove)
								throw new Exception(" use += or -= for reference " + en.Value);

							Correlation c = (Correlation) query.UpdatePath.Correlations[en.Value];
							if(c == null)
							{
								c = (Correlation) query.UpdatePath.Correlations[query.ModificationEntity];
								if(!c.NeededRelations.ContainsKey(rd))
								{
									c.NeededRelations.Add(rd, en2);
								}
							}

							IReferenceMappingStrategy refMapS = (IReferenceMappingStrategy) rd.GetService(typeof(IReferenceMappingStrategy));
							ExpressionName enRef = (ExpressionName) refMapS.GetRelationalExpression(null, en2.Value, string.Empty);

							RelationalOperator op = RelationalQueryGenerator.GenerateOperator(mappingKind, query.Path);

							UpdateQuery.UpdateQueryTypes utype = UpdateQuery.UpdateQueryTypes.Insert;
						
							if(se.SetExpressionMode == SetExpressionModes.Remove)
								utype = UpdateQuery.UpdateQueryTypes.Delete;
						
							newSe.SetExpressionName.Qualifier = rd.InverseRole;

							provider = (IEntityMappingStrategyProvider)rd.TargetData.GetService(typeof(IEntityMappingStrategyProvider));
							mappingStrategy = provider.GetMappingStrategy(mappingKind);
							ScanQuery sq = (ScanQuery) mappingStrategy.ProperInstancesQuery;

							UpdateQuery uq = new UpdateQuery(enRef.Qualifier
								, new SetExpression[] { seOid, newSe }
								,utype, op);

							alIntTable.Add(uq);
						}
					}
				}
			}

            List<RelationalQuery> alQueries = new List<RelationalQuery>();
            foreach (UpdateQuery uq in alIntTable)
            {
                alQueries.Add(uq);
            }
			
            if(uqDirectAcces != null)
				alQueries.Add(uqDirectAcces);

            foreach (UpdateQuery uq in htInverseAcces.Values)
            {
                alQueries.Add(uq);
            }

			return alQueries;
		}
		#endregion

		#region insert
		public static RelationalQuery GenerateQueryInsert(MappingStrategyEnum mappingKind, ObjectQuery query)
		{
			ScanPath sp = (ScanPath) query.Path;

			IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)sp.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
			
            IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
			ScanQuery sq = (ScanQuery) mappingStrategy.ProperInstancesQuery;

            List<DataColumn> alCols = new List<DataColumn>();
			foreach(ExpressionName en in query.InsertFields)
			{
				ExpressionNode expr2 = (ExpressionName) ExpressionMapper.Evaluate(en, sp);
				IList<SelectExpression> newCols;
				ExpressionName en2 = (ExpressionName) ExpressionExpander.Evaluate(expr2, sp, mappingKind, out newCols);
				DataColumn dci = new DataColumn();
				dci.ColumnName = en2.Value;
				alCols.Add(dci);
			}

			object values = query.InsertValues;
			if(values is ExpressionNode[])
			{
				ExpressionNode[] vNodes = (ExpressionNode[]) values;
				for(int i = 0; i < vNodes.Length; i++)
				{
					vNodes[i] = ExpressionMapper.Evaluate(vNodes[i], sp);
				}
				return new InsertQuery(sq.SourceName, alCols, (ExpressionNode[]) vNodes);
			}
			else
			{
				ObjectQuery oq = (ObjectQuery) values;
				RelationalQuery selectQuery = GenerateQuery(mappingKind, oq);
				return new InsertQuery(sq.SourceName, alCols, selectQuery);
			}			
		}
		#endregion

		#region delete
		public static RelationalQuery GenerateQueryDelete(MappingStrategyEnum mappingKind, ObjectQuery query)
		{
			ScanPath modPath = (ScanPath) query.UpdatePath;
			IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)modPath.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
			
            IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
			ICollection<EntityData> entityDatas = mappingStrategy.AllChildsArray;

            List<RelationalQuery> alResult = new List<RelationalQuery>();
			bool first = true;
			foreach(EntityData ed in entityDatas)
			{
				alResult.Add(generateQueryDelete(mappingKind, query, ed, first));
				first = false;
			}
			return new DeleteQuery(alResult);
		}

		private static DeleteQuery generateQueryDelete(MappingStrategyEnum mappingKind, ObjectQuery query, EntityData ed, bool first)
		{
			IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)ed.GetService(typeof(IEntityMappingStrategyProvider));
			
            IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
			ScanQuery sq = (ScanQuery) mappingStrategy.ProperInstancesQuery;
			
			RelationalOperator stdop = RelationalQueryGenerator.GenerateOperator(mappingKind, query.Path);

			provider = (IEntityMappingStrategyProvider) ed.GetService(typeof(IEntityMappingStrategyProvider));
			mappingStrategy = provider.GetMappingStrategy(mappingKind);						
						
			ExpressionName enOid = mappingStrategy.KeyMappingStrategy.GetOidExpression(sq.SourceName);

			return new DeleteQuery(sq.SourceName, stdop, enOid);			
		}
		#endregion

		public static RelationalQuery GenerateQuerySelect(MappingStrategyEnum mappingKind, ObjectQuery query)
		{
			ObjectPath op = query.Path;
			
			ObjectQuery[] unionQueries = query.UnionAllObjectQueries;
			if(unionQueries.Length > 0)
			{
				RelationalQuery resultUnion = null;
				for (int i = 0; i < unionQueries.Length; i++)
				{
					ObjectQuery objectQuery = unionQueries[i];
					RelationalQuery resultTemp = GenerateQuery(mappingKind, objectQuery);
					if(resultUnion == null)
						resultUnion = resultTemp;
					else
						resultUnion = new UnionQuery(resultUnion, resultTemp, resultUnion.DataColumns);
				}
				return resultUnion;
			}

			RelationalOperator result = RelationalQueryGenerator.GenerateOperator(mappingKind, op);
			if (query.SortOrder != null)
				result = EvaluateSort(result, query.SortOrder, op, mappingKind);
			ICollection<SelectExpression> qc = query.SelectColumns;
			if (qc == null)
			{
                List<SelectExpression> cl = new List<SelectExpression>();
				foreach(Correlation c in query.Path.Correlations.Values)
					cl.Add(new SelectExpression(new ExpressionName(c.Name, "*"), ""));
				qc = cl;
			}
            List<SelectExpression> columns = new List<SelectExpression>();
			foreach(SelectExpression c in qc)
			{
				ExpressionNode en = c.Expr;
				string alias = c.Alias;
				//string correlation = "";
				IList<SelectExpression> newCols;
				
				ExpressionNode expr2 = ExpressionMapper.Evaluate(en, op, result.Tag as IDictionary<string, RelationalElement>);

				en = ExpressionExpander.Evaluate(expr2, op, mappingKind, out newCols);
			
				foreach(SelectExpression sExpr in newCols)
				{
					columns.Add(sExpr);
				}

				if(en != null)
					columns.Add(new SelectExpression(alias, en));
			}
			if (query.GroupBy != null)
				result = EvaluateGroupBy(result
					, columns.ToArray() 
					, query.GroupBy
					, query.Having
					, op, mappingKind);
			
			RelationalQuery result2 = result.Project(columns);
			//ExpressionInheritedCorrelationFilter.Evaluate(result, (SelectExpression[]) columns.ToArray(typeof(SelectExpression)));

			result2.Distinct = query.Distinct;
			result2.TopCount = query.TopCount;
			return result2;
		}

		public static RelationalOperator GenerateOperator(MappingStrategyEnum mappingKind, ObjectPath path)
		{
			RelationalQueryGenerator e = new RelationalQueryGenerator(mappingKind);
			path.EvaluateFrom(e);
			RelationalOperator result = e.result;
			result.Tag = e.htCorrelationOperator;
			return result;
		}

		public static object GenerateArgs(object args)
		{
			if(args == null)
				return new object[0];

			if(args is IDictionary<string, object>)
				return GenerateArgs((IDictionary<string, object>) args);
			else if(args is System.Collections.ICollection)
				return GenerateArgs((System.Collections.ICollection) args);

			throw new NotSupportedException();
		}

		public static System.Collections.ICollection GenerateArgs(System.Collections.ICollection args)
		{
            System.Collections.ArrayList result = new System.Collections.ArrayList();
			if (args != null)
			{
				foreach(object arg in args)
				{
					result.Add(getValue(arg));
				}
			}
			return result;
		}

		public static IDictionary<string, object> GenerateArgs(IDictionary<string, object> args)
		{
            Dictionary<string, object> htResult = new Dictionary<string, object>(args.Count);

			if (args != null)
			{
                foreach(KeyValuePair<string, object> kvp in args)
                {
					htResult.Add(kvp.Key, getValue(kvp.Value));
				}
			}
			return htResult;
		}

		private static object getValue(object arg)
		{
			IEntity entity = arg as IEntity;
			if (entity != null)
			{
				object[] values = ((IRelationalEntityState)entity.State).Key.Values;

				if(values.Length == 1)
					return values[0];

				return values;
			}
			else if (arg is KeyValue)
			{
				return ((KeyValue)arg).Values;					
			}
			else if(arg is Enum)
			{
				return Convert.ToInt32(arg);
			}
			else if(arg is Array)
			{
				Array oldArray = (Array) arg;
				object[] vals = new object[oldArray.Length];
				for(int i = 0; i < oldArray.Length; i++)
				{
					vals[i] = getValue(oldArray.GetValue(i));
				}
				return vals;
			}
			else
				return arg;
		}
		

		protected MappingStrategyEnum mappingKind;
		protected RelationalOperator result;

		public RelationalQueryGenerator(MappingStrategyEnum mappingKind)
		{
			this.result = null;
			this.mappingKind = mappingKind;
		}

        private Dictionary<string, RelationalElement> htCorrelationOperator = new Dictionary<string, RelationalElement>(StringComparer.CurrentCultureIgnoreCase);

		public virtual void EvaluateScan(ScanPath a)
		{
			IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)a.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
			
            IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
			this.result = mappingStrategy.AllInstancesQuery.Rename(a.Alias);
			htCorrelationOperator[a.Alias] = this.result;
		}
	
		public virtual void EvaluateJoin(JoinPath j)
		{
			bool oldOuterJoin = InOuterJoin;
			InOuterJoin = j.IsOuterJoin;
			try
			{
				j.InnerQuery.EvaluateFrom(this);
			
				IEntityMappingStrategy targetMappingStrategy;
				IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)j.ReferenceData.TargetData.GetService(typeof(IEntityMappingStrategyProvider));
				targetMappingStrategy = provider.GetMappingStrategy(this.mappingKind);

				IReferenceMappingStrategyProvider referenceProvider = (IReferenceMappingStrategyProvider)j.ReferenceData.GetService(typeof(IReferenceMappingStrategyProvider));
				RelationalQuery targetQuery = targetMappingStrategy.AllInstancesQuery;
				htCorrelationOperator[j.Alias] = targetQuery;

				IKeyMappingStrategy targetKey = targetMappingStrategy.KeyMappingStrategy;
				this.result = referenceProvider.GetMappingStrategy(this.mappingKind).Join(j.IsOuterJoin, this.result, targetQuery, targetKey, j.Correlation.Name, j.Alias, j.Partial, j.Correlation.EntityData );
			}
			finally
			{
				InOuterJoin = oldOuterJoin;
			}
		}
		
		private bool inOuterJoin = false;

		public bool InOuterJoin 
		{ 
			get{ return inOuterJoin; }
			set{ inOuterJoin = value; }
		}

		public void EvaluateCross(CrossPath c)
		{
			IList<SelectExpression> newCols;

			bool oldOuterJoin = InOuterJoin;
			InOuterJoin = c.IsOuterJoin;
			try
			{
			
				ExpressionNode cond = null;
				if(c.JoinCondition != null)
				{
					ExpressionNode en = ConditionMapper.Evaluate(c.JoinCondition, c, MappingStrategyEnum.Remote2);
					cond = ExpressionExpander.Evaluate(en, c, MappingStrategyEnum.Remote2, out newCols);
				}
			
				c.Source1.EvaluateFrom(this);
				RelationalOperator temp = this.result;
				if(c.Source2 is ScanPath)
					((ScanPath) c.Source2).Alias = c.Alias;
				c.Source2.EvaluateFrom(this);
				
				this.result = temp.Cross(this.result, c.Alias, cond,  c.IsOuterJoin);
			}
			finally
			{
				InOuterJoin = oldOuterJoin;
			}
		}

		public void EvaluateScanQuery(ScanPathQuery s)
		{
			RelationalQuery rq = RelationalQueryGenerator.GenerateQuery(MappingStrategyEnum.Remote2, s.ScanQuery);
			this.result = new ScanQueryOperator(rq, s.Alias);
		}

		public void EvaluateCrossQuery(CrossPathQuery c)
		{
			bool oldOuterJoin = InOuterJoin;
			InOuterJoin = c.IsOuterJoin;
			try
			{
				c.Source1.EvaluateFrom(this);
			
				IList<SelectExpression> newCols;
				ExpressionNode en = ExpressionMapper.Evaluate(c.JoinCondition, c);
				ExpressionNode cond = ExpressionExpander.Evaluate(en, c, MappingStrategyEnum.Remote2, out newCols);
				RelationalOperator temp = this.result;
				RelationalQuery rq = RelationalQueryGenerator.GenerateQuery(MappingStrategyEnum.Remote2, c.Source2Query);
				this.result = temp.CrossQuery(rq, c.Alias, cond, c.IsOuterJoin);
			}
			finally
			{
				InOuterJoin = oldOuterJoin;
			}
		}
		
		class ConditionMapper: ExpressionCloner
		{
			public static ExpressionNode Evaluate(ExpressionNode n, ObjectPath context, MappingStrategyEnum mappingKind)
			{
				ConditionMapper es = new ConditionMapper(context, mappingKind);
				n.Accept(es);
				return es.result;
			}

			private ObjectPath context;
			
			public ConditionMapper(ObjectPath context, MappingStrategyEnum mappingKind)
			{
				this.context = context;
				this.mappingKind = mappingKind;
				inCorrelationFilteredNode = false;
			}

			private MappingStrategyEnum mappingKind;
			private bool inCorrelationFilteredNode;

			public override void VisitName(ExpressionName n)
			{
				if(n.Value.StartsWith("@") || inCorrelationFilteredNode)
				{
					this.result = new ExpressionName(n.Qualifier, n.Value);
					return;
				}

				if (n.Qualifier != "") 
				{
					if (this.context != null && n.Value != "*")
					{
						string qualifier = n.Qualifier;
						Correlation correlation = (Correlation)context.Correlations[qualifier];
						if (correlation == null)
							throw new Exception("qualifier " + n.Qualifier + " not found");
						string value = n.Value;
						
						if(correlation.EntityData.PropertyInfos.ContainsKey(value))
						{
							PropertyData propertyInfo = correlation.EntityData.PropertyInfos[value];
							
                            IPropertyMappingStrategy strategy = (IPropertyMappingStrategy)propertyInfo.GetService(typeof(IPropertyMappingStrategy));
							if(strategy != null)
								this.result = strategy.GetRelationalExpression(n.Qualifier);
							else
								this.result = new ExpressionName(n.Qualifier, n.Value);
						}
						else
						{
							if(correlation.EntityData.ReferenceInfos.ContainsKey(value))
							{
								ReferenceData referenceData = correlation.EntityData.ReferenceInfos[value];
								if(referenceData == null)
									throw new Exception("qualifier " + n.Qualifier + " not found");

								Correlation cJoin = (Correlation) correlation.LinkedCorrelations[referenceData];
								Correlation reverseCJoin = (Correlation) correlation.ReversedLinkedCorrelations[referenceData];
								IReferenceMappingStrategy strategy = (IReferenceMappingStrategy)referenceData.GetService(typeof(IReferenceMappingStrategy));
						
								if(cJoin != null)
								{
									this.result = strategy.GetRelationalExpression(n.Qualifier, value, cJoin.Name);
								}
								else if(reverseCJoin != null)
								{
									this.result = strategy.GetRelationalExpression(reverseCJoin.Name, value, n.Qualifier);
								}
								else
								{
									this.result = strategy.GetRelationalExpression(n.Qualifier, n.Value, n.Value);
								}
							}
							else
							{
								if(!correlation.NeededInverseRelations.Contains(value))
								{
									correlation.NeededInverseRelations.Add(value);
								}

								ReferenceData rd = null;

								if(this.context.Correlations.ContainsKey(value))
								{
									Correlation subc = (Correlation) this.context.Correlations[value];
									rd = subc.EntityData.ReferenceInfos[n.Qualifier];
								}
								if(rd == null)
								{
                                    Adoor.Object.Domain.EntityData ed = null;
                                    //TODO: ed = this.context.Domain.RootDomain.EntityInfos[value];
									if(ed != null)
									{
										rd = ed.ReferenceInfos[n.Qualifier];
									}									
								}
								if(rd == null)
								{
									base.VisitName(n);
								}
								else
								{
									IReferenceMappingStrategy strategy = (IReferenceMappingStrategy)rd.GetService(typeof(IReferenceMappingStrategy));
									this.result = strategy.GetRelationalExpression(n.Qualifier, n.Value, n.Value);
								}
							}
						}
					}
					else 
					{
						this.result = new ExpressionName(n.Qualifier, n.Value);
					}
					return;
				}
				else
				{
					string name = n.Value;
					Correlation s = (Correlation)this.context.Correlations[name];
					if (s == null)
						throw new Exception("correlation " + name + " not found");
					
					IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)s.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
					ExpressionNode[] columns = provider.GetMappingStrategy(this.mappingKind).KeyMappingStrategy.GetKeyExpression(name);
					if (columns.Length != 1)
						throw new Exception("OID composed of multiple columns cannot be used in a where clause");
					this.result = columns[0];
				}
			}

			public override void VisitInSubQuery(ExpressionInSubquery j)
			{
				j.Exp.Accept(this);
				ObjectQuery oq = (ObjectQuery) j.SubQuery;
				this.result = new ExpressionInSubquery(this.result, RelationalQueryGenerator.GenerateQuery(this.mappingKind, oq));
			}

			public override void VisitExistsSubQuery(ExpressionExistsSubquery j)
			{
				ObjectQuery oq = (ObjectQuery) j.SubQuery;
				this.result = new ExpressionExistsSubquery(RelationalQueryGenerator.GenerateQuery(this.mappingKind, oq));
			}

			public override void VisitCorrelationFilteredNode(ExpressionCorrelationFilteredNode c)
			{
                //TODO:
                //bool oldIn = this.inCorrelationFilteredNode;
                //try
                //{
                //    inCorrelationFilteredNode = true;
                //    Hashtable ht = System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable();
                //    foreach(DictionaryEntry entry in c.Nodes)
                //    {
                //        ExpressionNode node = (ExpressionNode) entry.Value;
                //        node.Accept(this);
                //        ht[entry.Key] = this.result;
                //    }
                //    c.DefaultNode.Accept(this);

                //    this.result = new ExpressionCorrelationFilteredNode(c.Qualifier, ht, this.result);
                //}
                //finally
                //{
                //    this.inCorrelationFilteredNode = oldIn;
                //}
			}

		}

		public void EvaluateFilter(FilterPath f)
		{
			f.InnerQuery.EvaluateFrom(this);
			ExpressionNode condition = ConditionMapper.Evaluate(f.Condition, f.InnerQuery, this.mappingKind);
			this.result = this.result.Filter(condition);
		}

		private static RelationalOperator EvaluateSort(RelationalOperator result, SortExpression[] sortOrder, ObjectPath path, MappingStrategyEnum mappingKind)
		{
            List<SortExpression> soList = new List<SortExpression>();
			foreach(SortExpression c in sortOrder)
			{
				ExpressionNode expr = c.Expr;
				if (expr is ExpressionName && ((ExpressionName) expr).Qualifier == "")
				{
					string name = ((ExpressionName) c.Expr).Value;
					Correlation correlation = (Correlation) path.Correlations[name];
					if (correlation == null)
						throw new Exception("illegal use of unqualified identifier: " + name);

					IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)correlation.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
					expr = provider.GetMappingStrategy(mappingKind).KeyMappingStrategy.GetOidExpression(name);
				}
				else 
				{
					IList<SelectExpression> newCols;
					expr = ExpressionExpander.Evaluate(c.Expr, path, mappingKind, out newCols);
					expr = ExpressionMapper.Evaluate(expr, path);
				}
				
				soList.Add(new SortExpression(expr, c.SortType));
			}
			SortExpression[] so = new SortExpression[soList.Count];
			soList.CopyTo(so);
			return result.Sort(so);
		}

		private static RelationalOperator EvaluateGroupBy(RelationalOperator result, SelectExpression[] select, GroupByExpression[] groupBy, HavingExpression having, ObjectPath path, MappingStrategyEnum mappingKind)
		{
			for(int i = 0; i < groupBy.Length; i++)
			{
				IList<SelectExpression> newCols;

				ExpressionNode expr2 = ExpressionMapper.Evaluate(groupBy[i].Expr, path);
				ExpressionNode en2 = ExpressionExpander.Evaluate(expr2, path, mappingKind, out newCols);

				groupBy[i].Expr = en2;
			}
			if(having != null)
				having.Expr = ConditionMapper.Evaluate(having.Expr, path, mappingKind);
			return result.GroupBy(result, select, groupBy, having);
		}
		
		public void EvaluateIntersect(IntersectPath i)
		{
			if (i.Correlation1 == i.Correlation2)
				throw new Exception("Both correlations have the same name: alias one of them");  // temp restriction: we could rename i.Source2
			i.Source1.EvaluateFrom(this);
			
            RelationalOperator result1 = this.result;

			this.result = null;
			i.Source2.EvaluateFrom(this);

			Correlation s1 = (Correlation)i.Source1.Correlations[i.Correlation1];
			if (s1 == null)
				throw new Exception("correlation " + i.Correlation1 + " not found");

			Correlation s2 = (Correlation)i.Source2.Correlations[i.Correlation2];

			if (s2 == null)
				throw new Exception("correlation " + i.Correlation2 + " not found");

			IEntityMappingStrategyProvider provider1 = (IEntityMappingStrategyProvider)s1.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
			IEntityMappingStrategyProvider provider2 = (IEntityMappingStrategyProvider)s2.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
			this.result = new IntersectOperator(result1, i.Correlation1, provider1.GetMappingStrategy(this.mappingKind).KeyMappingStrategy.PrimaryKey,
				this.result, i.Correlation2, provider2.GetMappingStrategy(this.mappingKind).KeyMappingStrategy.PrimaryKey, i.IsMinus);
		}
		
	}
}
